package sintatico.submaquinas;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.StringTokenizer;

import sintatico.ErroSintatico;
import sintatico.ResultadoTransicao;
import sintatico.InicioTransicao;
import symbols.Token;

import lexico.*;

public class SubMaquina {

	int estadoAtual;
	Hashtable<String, String> transicoes;
	LinkedList<Integer> estadosAceitacao;
	String arquivoSubmaquina;
	LexicalAnalyzer lexico;

	public SubMaquina(LexicalAnalyzer lexico) {
		
		estadosAceitacao = new LinkedList<Integer>();
		transicoes = new Hashtable<String, String>();
		estadoAtual = 0; // inicio

		this.lexico = lexico; 
	}

	/**
	 * Metodo que cria as tabelas de aceitacao e transicao, de acordo com os arquivos na pasta "submaquinas"
	 * OK
	 */
	protected void criarTabelas() {

		BufferedReader reader = null;
		
		try {
			reader = new BufferedReader(new FileReader(arquivoSubmaquina));
			String line = null;
			// Pega os estados de aceitacao, que estao na primeira linha
			line = reader.readLine();
			StringTokenizer estAceita = new StringTokenizer(line);
			while (estAceita.hasMoreTokens()) {
				estadosAceitacao.add(Integer.parseInt(estAceita.nextToken()));				
			}
			
			// Pegando as transicoes e colocando na hashtable
			while ((line = reader.readLine()) != null && !line.equals("")) {
				processaTabelaTransicoes(line);				
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	/**
	 * 
	 * @param line gera na tabela a linha atual
	 * ok
	 */
	private void processaTabelaTransicoes(String line) {
		
		Integer estadoAtual = null;
		Token token = null;
		Integer proximoEstado = null;
		String submaquina = null;
		Integer acaoSemantica = null;

		String[] subString = null;

		/*
		 * a linha esta dividida em
		 * [0] - estado atual
		 * [1] - token ou chamada de submaquina
		 * [2] - proximo estado
		 * [3] - acao semantica a ser realizada
		 */
		
		subString = line.split(" ");

		// Seta o estado proveniente
		try {
			estadoAtual = Integer.parseInt(subString[0]);
		} catch (NumberFormatException ex) {
			estadoAtual = null;
		}
		
		// Seta ou o token ou a submaquina
		try {
			if (subString[1].substring(0, 1).equals("\"")) {// Se comeca com ", entao eh palavra reservada 
				// lower case para comparacao na tabela de palReservadas
				// pega ja tirando as aspas das palavras reservadas dadas pelo arquivo
				token = lexico.getPalReservada().get(subString[1].toLowerCase().substring(1, subString[1].length()-1));
				if (token == null) {
					new ErroSintatico(subString[1].toLowerCase().substring(1, subString[1].length()-1)+": token nao encontrado na lexico.palReservada, e deveria estar",lexico.getLineNumber());
				}
			}
			else {// se nao comeca com " eh uma submaquina, numero ou id
				 if(subString[1].equals("programa") || subString[1].equals("comando") ||
						 subString[1].equals("expressao") || subString[1].equals("funcao")){
					 submaquina = subString[1];
				 }
//				 else if (subString[1].equals("id")){
//					 submaquina = String.valueOf(Tag.ID);
////					 submaquina = "id";
//				 }
//				 else if (subString[1].equals("num")){
//					 submaquina = String.valueOf(Tag.NUMBER);
////					 submaquina = "num"
//				 }
				 else {
					 new ErroSintatico("Algo invalido.", lexico.getLineNumber());
				 }
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			proximoEstado = Integer.parseInt(subString[2]);
		} catch (NumberFormatException ex) {
			proximoEstado = null;
		}
		
		try {
			acaoSemantica = Integer.parseInt(subString[3]);
		} catch (NumberFormatException ex) {
			acaoSemantica = null;
		}
		
		InicioTransicao transicao = null;
		if (token != null) {
			transicao = new InicioTransicao(estadoAtual, token.tag);
		} else {
			transicao = new InicioTransicao(estadoAtual, null);
		}

		ResultadoTransicao resultado = new ResultadoTransicao(proximoEstado, submaquina, acaoSemantica);

		transicoes.put(transicao.getEstadoAtual()+"%"+transicao.getIdTokenAtual(), 
				resultado.getProximoEstado()+"%"+resultado.getSubmaquina()+"%"+resultado.getAcaoSemantica());
	}

	/**
	 * 
	 * @param token
	 * @return
	 */
	public ResultadoTransicao fazTransicao(Token token) {
		InicioTransicao transicao = null;
		
		if (token != null) {
			transicao = new InicioTransicao(estadoAtual, token.tag);
		} else {
			transicao = new InicioTransicao(estadoAtual, null);
		}
		
		ResultadoTransicao resultTrans ;
		String aux;
		
		if(transicao.getIdTokenAtual() != null){
			aux = transicoes.get(transicao.getEstadoAtual()+"%"+transicao.getIdTokenAtual());
//			System.out.println(transicao.getEstadoAtual()+"%"+transicao.getIdTokenAtual());
		} else {
			aux = transicoes.get(transicao.getEstadoAtual()+"%null");
		}
//		System.out.println("aux=" + aux);
		if(aux==null){
			return null;
		}
		
		String[] substring = aux.split("%");
		int proximoEstado = Integer.parseInt(substring[0]);
		String submaquina;
		
		if(substring[1].equals("null")){
			submaquina=null;
		} else{
			submaquina=substring[1];
		}
		
		Integer acaoSemantica;
		
		if(substring[2].equals("null")){
			acaoSemantica=null; // nao necessita de acao semantica
		} else{
			acaoSemantica=Integer.parseInt(substring[2]);
		}
		
		resultTrans = new ResultadoTransicao(proximoEstado,submaquina,acaoSemantica);
		return resultTrans;
	}
	
	public static SubMaquina geraSubMaquina (String submaquina, LexicalAnalyzer lex) {
//		System.out.println(submaquina);
		if (submaquina.equals("programa")) {
			return new Programa(lex);
		}
		else if (submaquina.equals("comando")) {
			return new Comando(lex);
		}
		else if (submaquina.equals("expressao")) {
			return new Expressao(lex);
		}
		else if (submaquina.equals("funcao")) {
			return new Funcao(lex);
		}
		return null;
	}
	
	public void imprimeTrasicoes() {
		Collection<String> transV = this.transicoes.values();
		Enumeration<String> transK = this.transicoes.keys();
		Iterator<String> itr = transV.iterator();
		while(itr.hasNext())
		      System.out.println("Chave: "+transK.nextElement()+" Valor: "+itr.next());
	}

	public boolean isEstadoAceitacao() {
		return estadosAceitacao.contains(estadoAtual);
	}

	public int getEstadoAtual() {
		return estadoAtual;
	}

	public void setEstadoAtual(int estadoAtual) {
		this.estadoAtual = estadoAtual;
	}

	public boolean verificaTransicao() {
		return false;
	}

}